package com.swak.consistency.core.context;

import java.util.Map;
import java.util.Stack;

import org.springframework.util.Assert;

import com.swak.annotation.ConsistencyScheme;
import com.swak.utils.Maps;

/**
 * 绑定线程中的事务
 * 
 * @author DELL
 */
public class ThreadContext {

	private static final ThreadContext CONTEXT = Holder.INSTANCE;
	private final ThreadLocal<ContextStacks> resources = new ThreadLocal<>();

	private static class Holder {
		private static final ThreadContext INSTANCE = new ThreadContext();
	}

	public static ConsistencyContext get(ConsistencyScheme scheme) {
		Assert.isTrue(scheme != null, "scheme Can Not Be Null");
		return CONTEXT._get(scheme);
	}

	/**
	 * 设置到当前的线程
	 * 
	 * @param context
	 */
	static void bind(ConsistencyContext context) {
		Assert.isTrue(context.getScheme() != null, "Scheme Can Not Be Null");
		CONTEXT._put(context.getScheme(), context);
	}

	/**
	 * 释放： 和 bind 必须成对出现
	 */
	static void remove(ConsistencyContext context) {
		Assert.isTrue(context.getScheme() != null, "Scheme Can Not Be Null");
		CONTEXT._remove(context.getScheme());
	}

	ConsistencyContext _get(ConsistencyScheme scheme) {
		ContextStacks stacks = resources.get();
		if (stacks != null && !stacks.contexts.isEmpty()) {
			return stacks.get(scheme);
		}

		else if (stacks != null) {
			stacks.contexts.clear();
			resources.remove();
		}
		return null;
	}

	/**
	 * 添加属性
	 * 
	 * @param key
	 * @param value
	 */
	ContextStacks _put(ConsistencyScheme scheme, ConsistencyContext context) {
		if (scheme == null) {
			throw new IllegalArgumentException("Scheme cannot be null");
		}
		ContextStacks stacks = resources.get();
		if (stacks == null) {
			stacks = new ContextStacks();
			resources.set(stacks);
		}
		return stacks.set(scheme, context);
	}

	/**
	 * 添加属性
	 * 
	 * @param key
	 * @param value
	 */
	void _remove(ConsistencyScheme scheme) {
		if (scheme == null) {
			throw new IllegalArgumentException("Scheme cannot be null");
		}
		ContextStacks stacks = resources.get();
		if (stacks != null) {
			stacks.remove(scheme);
			if (stacks.contexts.isEmpty()) {
				resources.remove();
			}
		}
	}

	static class ContextWrap {
		ConsistencyContext context;
		int depth;
		ContextBindAttr bindAttr = new ContextBindAttr();

		ContextWrap(int depth, ConsistencyContext context) {
			this.depth = depth;
			this.context = context;
			this.context.setBindAttr(bindAttr).setDepth(depth);
		}

		void bindContextAttr() {
			this.context.setDepth(depth);
			this.context.setBindAttr(bindAttr);
		}
	}

	static class ContextStacks {
		Map<ConsistencyScheme, Stack<ContextWrap>> contexts;

		ContextStacks() {
			contexts = Maps.newHashMap(2);
		}

		ContextStacks set(ConsistencyScheme scheme, ConsistencyContext context) {
			Stack<ContextWrap> stack = contexts.get(scheme);
			if (stack == null) {
				stack = new Stack<ContextWrap>();
				contexts.put(scheme, stack);
			}
			int depth = stack.size();
			stack.push(new ContextWrap(depth, context));
			return this;
		}

		ConsistencyContext get(ConsistencyScheme scheme) {
			Stack<ContextWrap> stack = contexts.get(scheme);
			if (stack == null) {
				return null;
			}
			ContextWrap wrap = stack.peek();
			return wrap.context;
		}

		void remove(ConsistencyScheme scheme) {
			Stack<ContextWrap> stack = contexts.get(scheme);
			if (stack == null) {
				return;
			}
			stack.pop();
			if (stack.isEmpty()) {
				contexts.remove(scheme);
			} else {
				stack.peek().bindContextAttr();
			}
		}
	}
}
